package command

import (
	"archive/zip"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"time"

	"gitee.com/csingo/cHelper"
	"gitee.com/csingo/csingo/config/vars"
	"github.com/schollz/progressbar/v3"
)

type ToolCommand struct{}

func (i *ToolCommand) Desc() *CommandDesc {
	return &CommandDesc{
		Name: "tool",
		Methods: []*CommandMethod{
			{
				Name:   "init",
				Desc:   "初始化工具",
				Params: []*CommandParams{},
			},
		},
	}
}

func (i *ToolCommand) Init() {
	var err error

	err = i.installProtoc()
	if err != nil {
		log.Panic(err)
	}

	err = i.installGoPlugins()
	if err != nil {
		log.Panic(err)
	}
}

func (i *ToolCommand) installProtoc() (err error) {
	gopath := GetGOENV("GOPATH")
	goos := GetGOENV("GOOS")

	var download, dPath, fPath, fBackupPath string

	dPath = filepath.Join(gopath, "protoc.zip")

	switch goos {
	case "linux":
		download = vars.Tool.ProtocDownload.Linux
		fPath = filepath.Join(gopath, "bin", "protoc")
		fBackupPath = filepath.Join(gopath, "bin", "protoc.bak")
	case "windows":
		download = vars.Tool.ProtocDownload.Win
		fPath = filepath.Join(gopath, "bin", "protoc.exe")
		fBackupPath = filepath.Join(gopath, "bin", "protoc.exe.bak")
	case "darwin":
		download = vars.Tool.ProtocDownload.Mac
		fPath = filepath.Join(gopath, "bin", "protoc")
		fBackupPath = filepath.Join(gopath, "bin", "protoc.bak")
	}

	// 备份
	if cHelper.IsExistsPath(fPath) {
		err = os.Rename(fPath, fBackupPath)
		if err != nil {
			return err
		}
	}

	// 还原备份
	defer func() {
		if err != nil {
			log.Println(err)
			if cHelper.IsExistsPath(fBackupPath) {
				if cHelper.IsExistsPath(fPath) {
					e := os.Remove(fPath)
					if e != nil {
						err = e
						return
					}
				}
				e := os.Rename(fBackupPath, fPath)
				if e != nil {
					err = e
					return
				}
			}
		} else {
			if cHelper.IsExistsPath(fBackupPath) {
				e := os.Remove(fBackupPath)
				if e != nil {
					err = e
					return
				}
			}
		}
	}()

	// 下载
	log.Println("下载 protoc.zip ...", download)
	if cHelper.IsExistsPath(dPath) {
		err = os.Remove(dPath)
		if err != nil {
			return
		}
	}
	downloadRsp, err := http.Get(download)
	defer func() {
		e := downloadRsp.Body.Close()
		if e != nil {
			err = e
			return
		}
	}()
	if err != nil {
		return
	}
	f, err := os.Create(dPath)
	if err != nil {
		return
	}
	bar1 := progressbar.NewOptions64(
		downloadRsp.ContentLength,
		progressbar.OptionSetDescription("download"),
		progressbar.OptionSetWriter(os.Stdout),
		progressbar.OptionShowBytes(true),
		progressbar.OptionSetWidth(100),
		progressbar.OptionThrottle(65*time.Millisecond),
		progressbar.OptionShowCount(),
		progressbar.OptionOnCompletion(func() {
			fmt.Printf("\n")
		}),
	)
	err = bar1.RenderBlank()
	if err != nil {
		return err
	}
	_, err = io.Copy(io.MultiWriter(f, bar1), downloadRsp.Body)
	if err != nil {
		return
	}

	// 解压 protoc
	log.Println("解压 protoc.zip ...", dPath)
	time.Sleep(1 * time.Second)
	zipReader, err := zip.OpenReader(dPath)
	if err != nil {
		return
	}
	for _, zf := range zipReader.File {
		if zf.Name == "bin/protoc.exe" || zf.Name == "bin/protoc" {
			var inFile io.ReadCloser
			var outFile *os.File

			inFile, err = zf.Open()
			if err != nil {
				return
			}

			outFile, err = os.OpenFile(fPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, zf.Mode())
			if err != nil {
				return
			}

			_, err = io.Copy(outFile, inFile)
			if err != nil {
				return
			}

			if err = inFile.Close(); err != nil {
				return
			}
			if err = outFile.Close(); err != nil {
				return
			}

			break
		}
	}
	err = zipReader.Close()
	if err != nil {
		return
	}

	// 删除protoc.zip
	//log.Println("删除 protoc.zip ...", dPath)
	//time.Sleep(10 * time.Second)
	//err = os.Remove(dPath)
	//if err != nil {
	//	return
	//}

	return
}

func (i *ToolCommand) installGoPlugins() (err error) {
	gopath := GetGOENV("GOPATH")
	initPath := filepath.Join(gopath, fmt.Sprintf(".csingo-init-%s", time.Now().Format("20060102150405")))
	if !cHelper.IsExistsPath(initPath) {
		err = os.MkdirAll(initPath, os.ModePerm)
		if err != nil {
			return
		}
	}

	defer func() {
		err = os.RemoveAll(initPath)
		if err != nil {
			return
		}
	}()

	Exec("go mod init csingo-init", initPath)
	Exec("go mod tidy", initPath)

	log.Println("安装 protoc-gen-go ...")
	Exec(fmt.Sprintf("go install %s", vars.Tool.ProtoGenGoPackage), initPath)

	log.Println("安装 protoc-gen-go-grpc ...")
	Exec(fmt.Sprintf("go install %s", vars.Tool.ProtoGenGoGrpcPackage), initPath)

	log.Println("安装 goimports ...")
	Exec(fmt.Sprintf("go install %s", vars.Tool.GoImportsPackage), initPath)

	log.Println("安装 go-bindata ...")
	Exec("go get github.com/jteeuwen/go-bindata/...", initPath)
	Exec("go install github.com/jteeuwen/go-bindata/...", initPath)

	log.Println("安装 go-bindata-assetfs ...")
	Exec("go get github.com/elazarl/go-bindata-assetfs/...", initPath)
	Exec("go install github.com/elazarl/go-bindata-assetfs/...", initPath)

	log.Println("安装 golangci-lint ...")
	Exec(fmt.Sprintf("go get %s", vars.Tool.GoLintPackage), initPath)
	Exec(fmt.Sprintf("go install %s", vars.Tool.GoLintPackage), initPath)

	return
}
